home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Hacker's Secrets 4
/
Hacker's Secrets 4.iso
/
misc
/
telnet.txt
< prev
next >
Wrap
Text File
|
1996-06-23
|
26KB
|
631 lines
**************************************************************************
TIP: Telnetd security vulnerability
System:
Source: Sam Hartman, MIT Kerberos Development team
Date: 15 Oct 1995
**************************************************************************
Contents
* Preface and History
* Quick Fix
* Environment Variables that Matter
* Affected Telnetds
* Telnetds that Work
* Availability of Patches
* Testing for Exposure
* Verifying a Patch
* Sample Patch
* Acknowledgments
Preface and History
On Sunday, October 15, I discovered a bug in some versions of
telnetd on some platforms that allows a user making a connection to
cause login to load an alternate C library from an arbitrary location
in the filesystem of the machine running telnetd. In the case of
machines mounting distributed filespaces such as AFS or NFS,
containing publicly writable anonymous FTP directories, or on which
the user already has a non-root account, it is possible to gain root
access.
The problem is that telnetd will allow the client to pass
LD_LIBRARY_PATH, LD_PRELOAD, and other run-time linker options into the
process environment of the process that runs login. If the runtime
linker honors these options for login, the attacker can cause a custom
libc to be loaded. Such a libc could, for example, start a shell
whenever some function like crypt() is called. If login calls this
function before the setuid call, then the attacker will gain root
access.
Note that the user must be able to convince telnetd to run
login in order for this attack to be successful. In particular, if an
authentication system such as Kerberos is employed, and the telnetd
requires authentication, then only users with valid accounts will be
able to use this attack.
Quick Fix
Normally, programs that run with the set-user-ID or
set-group-ID bit set do not use environment variables to pass
information about where to find libraries. This is designed to
prevent attacks where an intruder sets LD_LIBRARY_PATH and runs `su'
or `login' from the command line. Since these are set-user-ID
programs, they run as root; if they trust LD_LIBRARY_PATH, then they
can load a user-supplied libc and be as insecure as telnetd. The test
used by most runtime linkers to determine if LD_LIBRARY_PATH can be
trusted checks to see if the effective user ID is equal to the real
user ID. Since telnet is started as a root-owned process by inetd,
its real user ID is root. So, even if login is set-user-ID, the test
succeeds and LD_LIBRARY_PATH is trusted, creating the security
problem.
On many systems, if login is made set-group-ID, the test will
fail because login's effective group will be different than the real
group. This should only be used on a temporary basis. Unfortunately,
this doesn't always work: in particular, it doesn't work for SGI Irix.
(SGI has already released a patch, but other systems may exist where
this also fails.) If you try this fix, you should go through the
"Testing for Exposure" section.
To make login set-group-ID follow these steps:
1) Create a new group (you might want to call it `__login',
`__telnet', `tnbug' or something of the sort). In the rest of this
document, I will assume that you have called the group `__login'.
Make sure the group doesn't already exist, and make sure that no
other programs are moved into this group. For information on how
to create a group, consult your vendor's manuals and the man page
for /etc/group.
2) Find your login program; it is often /usr/bin/login or /bin/login.
I will assume here that it is /bin/login. Under AIX and some other
operating systems, login may be a symlink to another program;change
the group of the actual program, not of the symlink.
3) Look at the current permissions on login:
bash$ ls -l /bin/login
lrwxrwxrwx 1 root system 13 Mar 8 1994 /bin/login ->
/usr/sbin/tsm
bash$ ls -lL /bin/login
-r-sr-xr-x 3 root security 59217 Aug 23 1994 /bin/login
bash$
Note that because I am running on an AIX system, I gave the -L
option to the second ls in order to trace through the symlink and find
the real login. You should remember what group login is in so you can
change things back after you get a vendor patch.
4) Change the group of login and set the set GID bit:
bash$ su
root's Password:
bash# chgrp __login /bin/login
bash# chmod g+s /bin/login
bash# ls -lL /bin/login
-r-sr-sr-x 3 root __login 59217 Aug 23 1994 /bin/login
bash#
Environment Variables that Matter
This is not an exhaustive list of environment variables
telnetd should filter, but it does contain several of the key
variables on systems used at MIT and by people with whom I have
consulted:
LD_LIBRARY_PATH: At least Solaris, SunOS, NetBSD, Linux and Digital
Unix use this as the path to look for shared libraries in.
LD_PRELOAD: Solaris and possibly others load these object modules into
the address space of the process before loading other shared
libraries.
LIBPATH: AIX uses this to locate its shared libraries.
ELF_LD_LIBRARY_PATH: May be used by the Linux Elf loader; similar to
LD_LIBRARY_PATH in function. According to the author of Linux
ld.so (hjl@nynexst.com), this was only used by ld.so version 2.6.
This version was only used for beta Elf development, and is
apparently not used by any production Linux distributions.
LD_AOUT_LIBRARY_PATH: Another Linuxism from ld.so-1.7.3. Same as
LD_LIBRARY_PATH but only for a.out libraries.
_RLD_ROOT: Digital Unix uses this to specify a prefix prepended to
library paths stored inside executables.
_RLD_LIST: A list of objects dynamically loaded into an executable by
Digital Unix.
_RLD_*: Used by Digital Unix and SGI. There are several apparently
undocumented variables in /sbin/loader on Digital Unix and in the
SGI runtime linker.
LD_*: Several other variables have special meaning to certain
operating systems. Stripping all these variables would probably be
a good idea.
IFS: It is possible that setting IFS could cause damage in
environments where the user logs into an account that runs a shell
script instead of granting full access.
Affected Telnetds
All telnetds derived from the Telnet package distributed by
David Borman allow the environment options to be passed. Borman has
released a patch for the problem as of October 19. The patch released
on October 19, while secure, has a bug that prevents any telnet
environment options from being handled. Another patch was released on
October 23 that appears to work; see below for details. Besides his
original release, here are a list of operating systems and security
packages I'm aware of that include derivatives of this work:
* NetBSD and FreeBSD are distributed with a vulnerable
telnetd. (See below for patch info.)
* The version of telnetd maintained in the Kerberos version 5
distribution by MIT. (patch available)
* The Cygnus Network Security V4 95Q1 Free Network Release includes a
vulnerable telnetd. (Previous releases did not contain telnetd.) A
patch has been released.
* OpenVision's OV*Secure contains a telnetd that is vulnerable; a
patch is available.
Other Vulnerable Telnetd Implementations
This problem is not unique to code derived from the Borman
telnet distribution. Other vulnerable implementations are known to
include:
* SGI Irix 5.3 (patch available)
* Digital Unix. The telnetd distributed with stock Digital Unix
appears to be vulnerable. DEC confirms they are investigating.
* Linux. The telnetd distributed with Slackware Linux appears to be
vulnerable, although I have not verified this. The maintainers of
Debian GNU/Linux confirm their telnetd is vulnerable and released a
patch; see below. A patch is also available for Redhat Linux.
Telnetds that Work
Below is a list of operating systems which come with telnetds
that we know are not vulnerable.
* SunOS 4.1.4. The Sunos 4.1.4 telnetd does not support passing of
environment variables, so it is not vulnerable.
* IBM AIX 4.1. This telnetd does not support environment options.
* BSDI's BSD/OS. While the telnetd will pass any environment option,
there doesn't appear to be an option to override the shared library
path, so BSD/OS is probably not vulnerable. On October 19, Dave
Borman <dab@cray.com> confirmed that BSDI is not vulnerable to the
attack, although the telnetd will accept any environment variable.
* Telnetd on other systems that do not support shared libraries.
This includes DEC Ultrix, and Cray Unicos.
* According to LaMont Jones <lamont@cranston.fc.hp.com>, "HP-UX is not
vulnerable to this attack, due to our shared library
implementation."
Note that both AIX and SunOS can be vulnerable if the stock
telnetd is replaced. Also, note that the stock Solaris telnetd has
not been tested.
Availability of Patches
This is a list of patches I'm aware of at this time. As you
develop a patch for your product/platform, please let me know;
considering free operating systems affected by this problem, widely
announcing the bug once patches are available is very important. Note
that these patches are provided as-is, without any guarantee of
correctness or security on the part of MIT, myself, or the patch
creator. They are provided in a spirit of cooperation, not as a
guaranteed fix. I cannot certify the degree of testing applied to
these patches. Note that CERT and CIAC plan to announce bulletins
about this problem on October 31, 1995. Where this memo conflicts
with the information provided in the CERT advisory, assume
CERT's information is more accurate: they have better vendor contacts,
and have been actively confirming patch availability for the last few
days.
I am including this section in order to provide users with
patch locations, where possible, in the same place where they first
encounter details about this problem.I am maintaining it with
information I receive, but not all vendors have replied to my earlier
memo, so if your favorite vendor isn't listed here, check the CERT
advisory before contacting them.
* On October 19, David Borman <dab@cray.com> released a new version of
his telnet package, containing a fix to the problem. This original
patch disabled passing environment options entirely, but was revised
on October 23. The revised patch, and instructions for obtaining it
are contained at the bottom of this message. Note that this patch
does not deal with the ELF_LD_LIBRARY_PATH, although for most Linux
users, this is not a problem. The version of telnet on
net-dist.mit.edu contains this patch.
* Greg Hudson <ghudson@mit.edu> checked a patch into the
NetBSD-current source tree. This patch will be incorporated by any
NetBSD-current users who update to the current telnetd. It will be in
the NetBSD 1.1 release. NetBSD developers have not indicated whether
they plan on releasing a patch for NetBSD 1.0 users. Note that the
sample NetBSD patch distributed with an earlier version of the memo
was incomplete; the version in the source tree as of October 18 is
correct.
* Sam Hartman <hartmans@mit.edu> patched the upcoming release of
Kerberos 5. In addition, patches were generated against Kerberos 5
beta 5 and beta 4-3. The can be found at
ftp://athena-dist.mit.edu/pub/kerberos/telnet-patch/.
* Mark Eichin <eichin@cygnus.com> prepared patches for CNS. These
patches will be available on the Cygnus web site
http://www.cygnus.com/data/cns/telnetdpatch.html; support customers
are being contacted directly.
* OpenVision has a patch for the telnetd in OV*Secure 1.2 and will
contact its customers directly.
* Peter Tobias, <tobias@et-inf.fho-emden.de> released a patch for
Debian GNU/Linux. This patch can be found in the networking
utilities at
ftp://ftp.debian.org/debian/debian-0.93/binary/net/netstd-1.21-1.deb.
* Erik Troan <ewt@redhat.com> confirms that Redhat Linux is
vulnerable, indicating a patch can be found at
ftp://ftp.redhat.com/pub/redhat-2.0/updates/NetKit-B-0.06-4.i386.rpm
or
ftp://ftp.pht.com/pub/linux/redhat/redhat-2.0/updates/NetKit-B-0.06-4.i386.rpm
The fix is incorporated into the Redhat 2.1 release.
* An SGI patch is available at ftp://sgigate.sgi.com/security/.
* DEC confirmed they will have a preliminary patch available on
October 31; they will be contacting customers and releasing patch info
to CERT.
* Andrey A. Chernov <ache@astral.msk.su> released a patch for FreeBSD,
but did not include an URL where the patch could be obtained.
* Bruce Lewis <brlewis@mit.edu> is preparing a patch for the
MIT-distributed Athena telnet/telnet95. His patch is currently
available within MIT. Within MIT, consult the netusers discuss
meeting for more details.
Testing for Exposure
In order to test to see if your telnetd passes environment
variables that effect shared libraries, it is important to understand
what environment variables are used by your runtime linker. See the
environment variables section for common examples. To be sure, you
should run strings over your runtime loader. For example, the
following shows the environment variables used by NetBSD:
athena% strings - /usr/libexec/ld.so
ld.so
LD_LIBRARY_PATH
LD_PRELOAD
LD_NOSTD_PATH
LD_SUPPRESS_WARNINGS
LD_WARN_NON_PURE_CODE
LD_NO_INTERN_SEARCH
.init
Cannot set breakpoint (%s)
Cannot re-protect breakpoint (%s)
LD_TRACE_LOADED_OBJECTS
Naturally, this is only an excerpt of the output. Therefore,
NetBSD probably honors the `LD_LIBRARY_PATH' variable. It appears to
honor several other variables as well. (In fact, it honors most of
the environment variables besides LD_PRELOAD, which hasn't been
implemented yet.) If a system were non-standard, it might not be easy
to know what was an environment variable and what was just a string in
the binary. For example, the string `runpath' in the Solaris loader
is not an environment variable, but a similar string `LIBPATH' in the
AIX kernel is the AIX environment variable. You also have to find the
dynamic loader, which isn't always easy. Look for a program called
`rld', `ld.so', `loader', or some similar name.
You should also check your vendor's documentation, but reading
the documentation should not be a substitute for reading the binaries,
for while binaries may deceive and obfuscate, they seldom lie.
Now that you know what environment variables to check for,
find out which telnetd your system runs. Note that the telnetd on my
system is almost certainly not in the same place as yours: this
session took place on a machine in the Athena environment, so it is
running a custom MIT telnetd. However, the same techniques should
work with `/etc/athena/telnetd' replaced with `/usr/sbin/in.telnetd'
or whatever is appropriate for your system.
athena% grep telnet /etc/inetd.conf
telnet stream tcp nowait root /etc/athena/telnetd telnetd -a off
athena%
Now, check to see if it looks like it handles environment
options at all (by grepping for `ENVIRON') and if it does anything
special with linker environment variables. This test is *not*
definitive: there are both false positives and negatives, but you can
get a general idea of what to expect in later steps.
athena% strings - /etc/athena/telnetd |grep ENVIRO
ENVIRON VALUE and VAR are reversed!
OLD-ENVIRON
NEW-ENVIRON
NEW-ENVIRON
athena% strings - /etc/athena/telnetd |grep LD_
athena%
Ok, it looks very much like I have a problem. My telnetd
appears to support environment options--it even has an error message
about it, but I see no mention of environment variables that should be
restricted. Note that even if I saw no environment info in telnetd, I
would continue with the test just to make sure.
For the next step, telnet to the machine and see if it passes
environment options such as LD_LIBRARY_PATH. Try to create a corrupt
libc.so in /tmp by creating a zero length file of the same name; if
the system is vulnerable, login will core dump when it tries to use
the new libc. If this test fails, try a test using `ps -e' to see if
the environment variable got set. In order to find out what library
to create, I'll use the `ldd' command on the executable; you could
also try looking through /lib, or under AIX, use `dump -H executable'.
athena% ldd /etc/athena/telnetd
/etc/athena/telnetd:
-lcurses.2 => /usr/lib/libcurses.so.2.1 (0x10032000)
-ltermcap.0 => /usr/lib/libtermcap.so.0.0 (0x1003d000)
-lutil.3 => /usr/lib/libutil.so.3.1 (0x1003f000)
-lc.12 => /usr/lib/libc.so.12.3 (0x10041000)
athena% touch /tmp/libc.so.12.3
Now, we try and connect:
athena% telnet
...including Athena's default telnet options: "-ax"
telnet> env define LD_LIBRARY_PATH /tmp:/var/tmp
telnet> env export LD_LIBRARY_PATH
telnet> set options
Will show option processing.
telnet> open vulnerable-machine
Trying 10.0.0.6...
Connected to telnet-bug-exploit.MIT.EDU.
Escape character is '^]'.
SENT WILL LFLOW
SENT WILL LINEMODE
SENT WILL NEW-ENVIRON
RCVD WILL SUPPRESS GO AHEAD
RCVD DONT LINEMODE
RCVD DO NEW-ENVIRON
SENT WONT XDISPLOC
RCVD DO OLD-ENVIRON
SENT WONT OLD-ENVIRON
SENT IAC SB TERMINAL-SPEED IS 9600,9600
RCVD IAC SB NEW-ENVIRON SEND
SENT IAC SB NEW-ENVIRON IS USERVAR "LD_LIBRARY_PATH" VALUE "/tmp:/var/tmp"
MIT SIPB NetBSD-Athena (xxx) (ttyp1)
ld.so: login: libc.so.12.3: Undefined error: 0
Connection closed by foreign host.
athena%
This machine is obviously vulnerable. Now, an example of what
happens if for some strange reason, login actually works, but the
machine is still potentially vulnerable: (telnet session as above, but
a login prompt)
telnet> open vulnerable-machine
Trying 10.0.0.6...
Connected to telnet-bug-exploit.MIT.EDU.
Escape character is '^]'.
MIT SIPB NetBSD-Athena (xxx) (ttyp1)
login:
Now, we suspend the telnet and look at the login process that
was created:
athena% ps -ewwa |grep login
6997 p1 Is+ 0:00.05 LD_LIBRARY_PATH=/tmp:/var/tmp TERM=vt100 login -h somew
athena%
This indicates that the variable was passed, but login failed
to act--possibly because you did something wrong when creating the
library; your system is probably still vulnerable. If that variable
was not present, but the -e flag works on your ps, and other processes
displayed environment variables, your system is likely not vulnerable.
Also, if neither an old-environ nor new-environ option was passed
between the telnetd and telnet, you are almost certainly safe.
However, passing this test should not be taken as a guarantee of
complete security: you should still contact your telnet vendor unless
you are sure you are safe.
Verifying a Patch
In the process of talking to vendors, distributing patches,
and getting feedback, I've come up with a lot of `almost solutions' --
patches that are good enough to make you think they work, but that can
be compromised.
* A clever trick to get around exact match patches is to embed an
equals sign in a variable name. For example, ask your client to send
an option requesting that the variable
LD_LIBRARY_PATH=/home/hartmans/exploits/sun4lib: be set to the value
invalid:/lib:/usr/lib. Naturally, the call to setenv in telnetd adds
another =, but that's soaked up by `invalid', and I still get to
break into the system. I.E. Deal with variable names containing
equals signs (=).
* At least in the Borman BSD telnet, there are two calls to setenv:
one for the last part of an environment option and one for the other
parts. Make sure you cover both; this was the biggest problem with
the sample patch I first distributed.
* If it is possible to stuff a string into the environment twice with
your telnetd, make sure you check all entries in the environment. For
example, if you have a setenv() that doesn't check for duplicates,
don't just use unsetenv() as this will remove the last item in the
environment, leaving the others to be used by login.
* Get all the important environment variables. Follow the
instructions for testing vulnerability, and check all the potential
environment variables found when you strings the loader.
Considering the potential to miss variables, several people have
suggested only allowing certain variables through. Borman is
investigating this and several other options; unfortunately,
anything less than a solution tailored to a particular vendor's
operating system decreases the functionality provided by the
environment option.
Sample Patch
Below, I include the official patch to telnet from David
Borman <dab@cray.com> as of October 23. Before the patch, I include a
message I received on October 19; this includes useful information.
As I received the message, it was not PGP-signed; its inclusion in
this signed summary indicates that it has not been modified since I
received it, and says nothing about the integrity of the
communications link between myself and Mr. Borman. However, I have
examined the patch, and it appears to be a valid fix for the bug. It
also corresponds to the appropriate sections of the diff on the ftp
server. Again, patches are provided as-is without a guarantee of
correctness; you assume all risk for applying this patch. (As with all
PGP-signed patches, you will need to remove leading dashes.)
Date: Thu, 19 Oct 95 13:54:56 CDT
From: dab@berserkly.cray.com (David A. Borman)
Message-Id: <9510191854.AA03474@frenzy.cray.com>
To: hartmans@MIT.EDU
Subject: Re: telnet vulnerability giving root access
Cc: cert@cert.org, tytso@MIT.EDU
I have placed a version of the BSD Telnet distribution at:
ftp://ftp.cray.com/src/telnet/telnet.95.10.23.NE.tar.Z
with a fix for this problem. It changes telnetd to remove the LD_*,
_RLD_*, IFS and LIBPATH environment variables before execing login.
The version on ftp.cray.com does not contain the encryption code,
that is on net-dist.mit.edu, and I have sent a new copy off to
them to replace the current distribution.
(The attached diffs also show a bugfix for a problem that was
screwing up /etc/utmp on Solaris.)
Also, BSDI is not affected, as they do not provide any way for
the user to override the search path for shared libraries.
UNICOS is unaffected, since we don't have shared libraries.
Please feel free to pass on this message.
-David Borman, dab@cray.com
diff -cbr telnet.95.05.31/telnetd/sys_term.c telnet.95.10.23/telnetd/sys_term.c
*** telnet.95.05.31/telnetd/sys_term.c Wed May 31 00:50:57 1995
- --- telnet.95.10.23/telnetd/sys_term.c Mon Oct 23 09:47:17 1995
***************
*** 32,38 ****
*/
#ifndef lint
! static char sccsid[] = "@(#)sys_term.c 8.4 (Berkeley) 5/30/95";
#endif /* not lint */
#include "telnetd.h"
- --- 32,38 ----
*/
#ifndef lint
! static char sccsid[] = "@(#)sys_term.c 8.4+1 (Berkeley) 5/30/95";
#endif /* not lint */
#include "telnetd.h"
***************
*** 1570,1579 ****
utmpx.ut_id[3] = SC_WILDC;
utmpx.ut_type = LOGIN_PROCESS;
(void) time(&utmpx.ut_tv.tv_sec);
! if (pututxline(&utmpx) == NULL)
! fatal(net, "pututxline failed");
#endif
/*
* -h : pass on name of host.
* WARNING: -h is accepted by login if and only if
- --- 1570,1581 ----
utmpx.ut_id[3] = SC_WILDC;
utmpx.ut_type = LOGIN_PROCESS;
(void) time(&utmpx.ut_tv.tv_sec);
! if (makeutx(&utmpx) == NULL)
! fatal(net, "makeutx failed");
#endif
+ scrub_env();
+
/*
* -h : pass on name of host.
* WARNING: -h is accepted by login if and only if
***************
*** 1809,1814 ****
- --- 1811,1836 ----
return(argv);
}
#endif /* NEWINIT */
+
+ /*
+ * scrub_env()
+ *
+ * Remove a few things from the environment that
+ * don't need to be there.
+ */
+ scrub_env()
+ {
+ register char **cpp, **cpp2;
+
+ for (cpp2 = cpp = environ; *cpp; cpp++) {
+ if (strncmp(*cpp, "LD_", 3) &&
+ strncmp(*cpp, "_RLD_", 5) &&
+ strncmp(*cpp, "LIBPATH=", 8) &&
+ strncmp(*cpp, "IFS=", 4))
+ *cpp2++ = *cpp;
+ }
+ *cpp2 = 0;
+ }
/*
* cleanup()
Acknowledgments
In preparing this bug summary, I have received the help of
several people. In particular, I would like to thank David Borman for
quickly fixing the problem once notified, and Bruce Lewis for
supplying a timely solution to the problem within MIT. In addition,
John Hawkinson <jhawk@mit.edu> provided help developing exploit
scripts and confirming that the bug existed on several systems. In
addition, I would like to thank vendor security contacts for being
responsive and working quickly to get patches ready as soon as
possible. I would also like to thank those at MIT who reviewed drafts
of this announcement and suggested improvements.